#include "Poco/UnicodeConverter.h"
#include "Poco/UTF8Encoding.h"
#include "Poco/Windows936Encoding.h"
#include "Poco/TextConverter.h"
#include "Poco/Exception.h"
#include "Poco/String.h"
#include "Poco/StringTokenizer.h"
#include "Poco/NumberParser.h"
#include "Encoding/Encoding.h"
#include "String.h"

using namespace SystemTool;

// Construct the std::string
String::String() :m_pWString(new std::wstring)
{

}

// Construct the std::string
String::String(std::string strData) :m_pWString(new std::wstring)
{
    std::wstring& strWData = *((std::wstring*)m_pWString);
    strWData = Encoding::UTF8ToUTF16(strData);
}

// Construct with utf16/utf32 string
String::String(std::wstring strData) :m_pWString(new std::wstring)
{
    std::wstring& strWData = *((std::wstring*)m_pWString);
    strWData = strData;
}

// Construct the std::string
String::String(char *pData) :m_pWString(new std::wstring)
{
    std::wstring& strWData = *((std::wstring*)m_pWString);
    strWData = Encoding::UTF8ToUTF16(pData);
}

// Construct the std::string
String::String(const char *pData) :m_pWString(new std::wstring)
{
    std::wstring& strWData = *((std::wstring*)m_pWString);
    strWData = Encoding::UTF8ToUTF16(pData);
}

// Construct the utf16/utf32 string
String::String(wchar_t *pData) :m_pWString(new std::wstring)
{
    std::wstring& strWData = *((std::wstring*)m_pWString);
    strWData = pData;
}

// Construct the utf16/utf32 string
String::String(const wchar_t *pData) :m_pWString(new std::wstring)
{
    std::wstring& strWData = *((std::wstring*)m_pWString);
    strWData = (wchar_t*)pData;
}

// Destruct the String
String::~String()
{
    *((std::wstring *)m_pWString) = L"";
    if(m_pWString)
    {
        delete (std::wstring*)m_pWString;
        m_pWString = nullptr;
    }
}

// Copy String
String::String(const String& other)
{
    m_pWString = new std::wstring;
    
    std::wstring& strWData = *((std::wstring*)m_pWString);
    std::wstring& strOtherWData = *((std::wstring*)other.m_pWString);
    strWData = strOtherWData;
}

// Assignment of String
String& String::operator=(const String& other)
{
    if(this != &other)
    {
        *((std::wstring *)m_pWString) = L"";
        if (m_pWString)
        {
            delete (std::wstring *)m_pWString;
            m_pWString = nullptr;
        }
        m_pWString = new std::wstring;

        std::wstring& strWData = *((std::wstring*)m_pWString);
        std::wstring& strOtherWData = *((std::wstring*)other.m_pWString);
        strWData = strOtherWData;
    }

    return *this;
}

// Get string data
std::string String::Data()
{
    std::wstring strData = *((std::wstring*)m_pWString);
    std::string strFinalData = Encoding::UTF16ToUTF8(strData);
    return strFinalData;
}

// Get wstring data
std::wstring String::WData()
{
    return *((std::wstring*)m_pWString);
}

// Is contain substring
bool String::IsContain(String strData)
{
    std::wstring strCurData = WData();
    std::wstring strOther = strData.WData();
    int iPos = strCurData.find(strOther);
    if(iPos == -1)
    {
        return false;
    }

    return true;
}

// Make upper
String String::MakeUpper()
{
    try
    {
        std::wstring strCurData = WData();
        std::wstring strFinalData = Poco::toUpper<std::wstring>(strCurData);
        return strFinalData;
    }
    catch(Poco::Exception& e)
    {
        std::cout << "Failed to make upper with err:" << e.displayText() << std::endl;
    }

    return *this;
}

// Make lower
String String::MakeLower()
{
    try
    {
        std::wstring strCurData = WData();
        std::wstring strFinalData = Poco::toLower<std::wstring>(strCurData);
        return strFinalData;
    }
    catch(Poco::Exception& e)
    {
        std::cout << "Failed to make lower with err:" << e.displayText() << std::endl;
    }

    return *this;
}

// Append data
String& String::Append(String strData)
{
    std::wstring& strCurData = *((std::wstring*)m_pWString);
    strCurData = strCurData.append(strData.WData());
    return *this;
}

// Append data
String& String::Append(int iData)
{
    std::wstring& strCurData = *((std::wstring*)m_pWString);
    strCurData = strCurData.append(std::to_wstring(iData));
    return *this;
}

// Append data
String& String::Append(unsigned int uData)
{
    std::wstring& strCurData = *((std::wstring*)m_pWString);
    strCurData = strCurData.append(std::to_wstring(uData));
    return *this;
}

// Append data
String& String::Append(long lData)
{
    std::wstring& strCurData = *((std::wstring*)m_pWString);
    strCurData = strCurData.append(std::to_wstring(lData));
    return *this;
}

// Append data
String& String::Append(unsigned long ulData)
{
    std::wstring& strCurData = *((std::wstring*)m_pWString);
    strCurData = strCurData.append(std::to_wstring(ulData));
    return *this;
}

// Append data
String& String::Append(long long llData)
{
    std::wstring& strCurData = *((std::wstring*)m_pWString);
    strCurData = strCurData.append(std::to_wstring(llData));
    return *this;
}

// Append data
String& String::Append(unsigned long long ullData)
{
    std::wstring& strCurData = *((std::wstring*)m_pWString);
    strCurData = strCurData.append(std::to_wstring(ullData)); 
    return *this;
}

// Append data
String& String::Append(float fData)
{
    std::wstring& strCurData = *((std::wstring*)m_pWString);
    strCurData = strCurData.append(std::to_wstring(fData));
    return *this;
}

// Append data
String& String::Append(double dData)
{
    std::wstring& strCurData = *((std::wstring*)m_pWString);
    strCurData = strCurData.append(std::to_wstring(dData));
    return *this;
}

// + string
String& String::operator+(String strData)
{
    return Append(strData);
}

// + string
String& String::operator+(int iData)
{
    return Append(iData);
}

// + string
String& String::operator+(unsigned int uData)
{
    return Append(uData);
}

// + string
String& String::operator+(long lData)
{
    return Append(lData);
}

// + string
String& String::operator+(unsigned long ulData)
{
    return Append(ulData);
}

// + string
String& String::operator+(long long llData)
{
    return Append(llData);
}

// + string
String& String::operator+(unsigned long long ullData)
{
    return Append(ullData);
}

// + string
String& String::operator+(float fData)
{
    return Append(fData);
}

// + string
String& String::operator+(double dData)
{
    return Append(dData);
}

// += string
String &String::operator+=(String strData)
{
    std::wstring& strCurData_Current = *((std::wstring*)m_pWString);
    std::wstring& strCurData_Another = *((std::wstring*)strData.m_pWString);
    strCurData_Current += strCurData_Another;
    return *this;
}

// += string
String &String::operator+=(int iData)
{
    std::wstring &strCurData_Current = *((std::wstring *)m_pWString);
    strCurData_Current += iData;
    return *this;
}

// += string
String &String::operator+=(unsigned int uData)
{
    std::wstring &strCurData_Current = *((std::wstring *)m_pWString);
    strCurData_Current += uData;
    return *this;
}

// += string
String &String::operator+=(long lData)
{
    std::wstring &strCurData_Current = *((std::wstring *)m_pWString);
    strCurData_Current += lData;
    return *this;
}

// += string
String &String::operator+=(unsigned long ulData)
{
    std::wstring &strCurData_Current = *((std::wstring *)m_pWString);
    strCurData_Current += ulData;
    return *this;
}

// += string
String &String::operator+=(long long llData)
{
    std::wstring &strCurData_Current = *((std::wstring *)m_pWString);
    strCurData_Current += llData;
    return *this;
}

// += string
String &String::operator+=(unsigned long long ullData)
{
    std::wstring &strCurData_Current = *((std::wstring *)m_pWString);
    strCurData_Current += ullData;
    return *this;
}

// += string
String &String::operator+=(float fData)
{
    std::wstring &strCurData_Current = *((std::wstring *)m_pWString);
    strCurData_Current += fData;
    return *this;
}

// += string
String &String::operator+=(double dData)
{
    std::wstring &strCurData_Current = *((std::wstring *)m_pWString);
    strCurData_Current += dData;
    return *this;
}

// Fill holder
static String FillPlaceHolder(String strSrcData, String strPlaceHolder, String strValue)
{
    if(strSrcData.IsEmpty() || strPlaceHolder.IsEmpty())
    {
        return "";
    }

    if (!strSrcData.IsContain(strPlaceHolder))
    {
        return strSrcData;
    }

    int iPos = strSrcData.Find(strPlaceHolder);
    if (iPos == -1)
    {
        return strSrcData;
    }

    return strSrcData.Replace(iPos, strPlaceHolder.Length(), strValue);
}

// Format args
String String::Args(String strData)
{
    return FillPlaceHolder(*this,"%s",strData);
}

// Format args
String String::Args(int iData)
{
    return FillPlaceHolder(*this, "%d", FromInt(iData));
}

// Format args
String String::Args(unsigned int uData)
{
    return FillPlaceHolder(*this,"%d",FromUInt(uData));
}

// Format args
String String::Args(long lData)
{
    return FillPlaceHolder(*this,"%d",FromLong(lData));
}

// Format args
String String::Args(unsigned long ulData)
{
    return FillPlaceHolder(*this,"%d",FromULong(ulData));
}

// Format args
String String::Args(long long llData)
{
    return FillPlaceHolder(*this,"%d",FromLong(llData));
}

// Format args
String String::Args(unsigned long long ullData)
{
    return FillPlaceHolder(*this,"%d",FromULLong(ullData));
}

// Format args
String String::Args(float fData)
{
    return FillPlaceHolder(*this,"%f",FromFloat(fData));
}

// Format args
String String::Args(double dData)
{
    return FillPlaceHolder(*this,"%f",FromDouble(dData));
}

// Equal
bool String::operator==(String& other)
{
    std::wstring strData = WData();
    std::wstring strOtherData = other.WData();
    return (strData == strOtherData);
}

// Not Equal
bool String::operator!=(String& other)
{
    return !(operator==(other));
}

wchar_t g_ch = L'N';
// Get a data
wchar_t String::operator[](int iIndex)
{
    if(iIndex < 0 || iIndex > Length())
    {
        return g_ch;
    }
    std::wstring strData = WData();
    return strData[iIndex];
}

// Is empty
bool String::IsEmpty()
{
    return WData().empty();
}

// Split the string by substring
bool String::Split(String strSep,std::vector<String>& vTable)
{
    if(strSep.IsEmpty())
    {
        return false;
    }
    std::wstring strData = WData();
    std::wstring strToken = strSep.WData();

    std::wstring strTemp;
    std::wstring strLast = strData;
    int iPos = strData.find(strToken);
    while (iPos != -1)
    {
        strTemp = strLast.substr(0,iPos);
        vTable.push_back(strTemp);
        strLast = strLast.substr(iPos + 1);
        iPos = strLast.find(strToken);
        strTemp.clear();
    }
    vTable.push_back(strLast);

    return true;
}

// Length of string
int String::Length()
{
    std::wstring strData = WData();
    return strData.length();
}

// Get substring
String String::SubStr(int iStartPos, int iSubLen)
{
    if(iStartPos < 0 || iStartPos > Length())
    {
        return *this;
    }
    std::wstring strData = WData();
    return strData.substr(iStartPos,iSubLen);
}

// Get substring
String String::SubStr(int iStartPos)
{
    if(iStartPos < 0 || iStartPos > Length())
    {
        return *this;
    }
    std::wstring strData = WData();
    return strData.substr(iStartPos);
}

// Get left string from start pos = 0
String String::Left(int iLength)
{
    if(iLength < 0)
    {
        return *this;
    }
    std::wstring strData = WData();
    return strData.substr(0,iLength);
}

// Get right string from end pos
String String::Right(int iLength)
{
    if(iLength < 0)
    {
        return *this;
    }
    std::wstring strData = WData();
    int iLen = strData.length();
    int iPos = iLen - iLength;
    return strData.substr(iPos);
}

// Find a substring
int String::Find(String strSubStr,int iStartPos)
{
    if(strSubStr.IsEmpty())
    {
        return -1;
    }
    std::wstring strData = WData();
    std::wstring strToken = strSubStr.WData();

    return (int)strData.find(strToken,iStartPos);
}

// Find last of sub string
int String::FindLastOf(String strSubStr)
{
    if(strSubStr.IsEmpty())
    {
        return -1;
    }
    std::wstring strData = WData();
    std::wstring strToken = strSubStr.WData();

    return (int)strData.find_last_of(strToken);
}

// Replace the sub string
String& String::Replace(String strOldStr,String strNewStr)
{
    if(strOldStr.IsEmpty())
    {
        return *this;
    }
    std::wstring strData = WData();
    std::wstring strOldData = strOldStr.WData();
    int iPos = strData.find(strOldData);
    int iLen = strOldData.length();
    while (iPos != -1)
    {
        *this = Replace(iPos,iLen,strNewStr);
        strData = WData();
        iPos = strData.find(strOldData);
    }
    return *this;
}

// Replace from pos
String& String::Replace(int iStartPos, int iReplaceLen,String strNewStr)
{
    if(iStartPos < 0 || iStartPos > Length())
    {
        return *this;
    }
    std::wstring strCurData = WData();
    std::wstring strNewData = strNewStr.WData();

    strCurData.replace(strCurData.begin() + iStartPos, strCurData.begin() + iStartPos + iReplaceLen, strNewData);
    *this = strCurData;
    return *this;
}

// To int
int String::ToInt()
{
    return atoi(Data().c_str());
}

// To unsigned int
unsigned int String::ToUInt()
{
    try
    {
        std::string strData = Data();
        return Poco::NumberParser::parseUnsigned(strData);
    }
    catch (Poco::Exception &e)
    {
        std::cout << "Failed to transfer to uint with err:" << e.displayText() << std::endl;
    }

    return 0;
}

// To long
long String::ToLong()
{
    char* pEnd = nullptr;
    return strtol(Data().c_str(), &pEnd, 10);
}

// To unsigned long
unsigned long String::ToULong()
{
    char* pEnd = nullptr;
    return strtoul(Data().c_str(), &pEnd, 10);
}

// To long long
long long String::ToLongLong()
{
    char* pEnd = nullptr;
    return strtoll(Data().c_str(), &pEnd, 10);
}

// To unsigned long long
unsigned long long String::ToULongLong()
{
    char* pEnd = nullptr;
    return strtoull(Data().c_str(), &pEnd, 10);
}

// To float
float String::ToFloat()
{
    char* pEnd = nullptr;
    return strtof(Data().c_str(), &pEnd);
}

// To double
double String::ToDouble()
{
    char* pEnd = nullptr;
    return strtod(Data().c_str(), &pEnd);
}

// From int 
String String::FromInt(int iData)
{
    return std::to_string(iData);
}

// From unsigned int 
String String::FromUInt(unsigned int uData)
{
    return std::to_string(uData);
}

// From long 
String String::FromLong(long lData)
{
    return std::to_string(lData);
}

// From unsigned long 
String String::FromULong(unsigned long ulData)
{
    return std::to_string(ulData);
}

// From long long 
String String::FromLLong(long long llData)
{
    return std::to_string(llData);
}

// From unsigned long long 
String String::FromULLong(unsigned long long ullData)
{
    return std::to_string(ullData);
}

// From flaot 
String String::FromFloat(float fData)
{
    return std::to_string(fData);
}

// From double
String String::FromDouble(double dData)
{
    return std::to_string(dData);
}
